package com.xqboss.apps.service.user;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xqboss.apps.constant.Config;
import com.xqboss.apps.constant.LockKey;
import com.xqboss.apps.domain.user.SysUserWallet;
import com.xqboss.apps.domain.user.SysUserWalletWithdraw;
import com.xqboss.apps.dto.user.GetWithdrawListDto;
import com.xqboss.apps.enums.user.UserWalletEnum;
import com.xqboss.apps.enums.user.WalletChangeTypeEnum;
import com.xqboss.apps.enums.user.WithdrawStatusEnum;
import com.xqboss.apps.mapper.user.SysUserWalletWithdrawMapper;
import com.xqboss.apps.vo.user.*;
import com.xqboss.common.core.domain.R;
import com.xqboss.common.core.redis.lock.LockValue;
import com.xqboss.common.core.redis.lock.RedisLock;
import com.xqboss.common.exception.ServiceException;
import com.xqboss.common.utils.StringUtils;
import com.xqboss.system.service.ISysConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * <p>
 * 用户钱包提现
 * </p>
 *
 * @author Trent
 * @date 2023/8/15
 */
@Service
public class SysUserWalletWithdrawService extends ServiceImpl<SysUserWalletWithdrawMapper, SysUserWalletWithdraw> {

    @Autowired
    private SysUserWalletService userWalletService;
    @Autowired
    private ISysConfigService configService;
    @Autowired
    private SysUserWalletWithdrawHandlerService userWalletWithdrawHandlerService;
    @Autowired
    private ISysConfigService sysConfigService;

    /**
     * 申请提现
     */
    @Transactional(rollbackFor = Exception.class)
    public void applyWithdraw(SysUserWalletWithdraw withdraw) {
        withdraw.setId(IdUtil.getSnowflakeNextId());
        withdraw.setStatus(WithdrawStatusEnum.WAIT_PROCESS);
        SysUserWallet wallet = userWalletService.getWallet(withdraw.getUserId(), UserWalletEnum.CASH);
        withdraw.setWalletId(wallet.getId());

        if(withdraw.getAmount().doubleValue() <= 0){
            throw new ServiceException("提现金额异常！");
        }
        if (wallet.getAmount().compareTo(withdraw.getAmount()) < 0) {
            throw new ServiceException("余额不足！");
        }
        BigDecimal dayMax = configService.getValue(Config.Sys.Withdraw.当日提现金额上限);
        if(Objects.nonNull(dayMax)){
            // 限制了日提现上限
            // 判断今日提现金额总数
            BigDecimal todayWithdraw = getSumWithdraw(withdraw.getUserId(), Arrays.asList(WithdrawStatusEnum.WAIT_PROCESS, WithdrawStatusEnum.PROCESS_PASS), LocalDate.now());
            if(todayWithdraw.add(withdraw.getAmount()).compareTo(dayMax) > 0){
                BigDecimal remainAmount = dayMax.subtract(todayWithdraw);
                if(remainAmount.compareTo(BigDecimal.ZERO) > 0){
                    throw new ServiceException("今日剩余可提现额度：" + remainAmount);
                }else{
                    throw new ServiceException("今日提现额度已用完");
                }
            }
        }
        // 计算总提现手续费
        BigDecimal totalWithdrawFee = BigDecimal.ZERO;
        // 使用的优惠额度
        BigDecimal useFreeAmount = BigDecimal.ZERO;
        BigDecimal withdrawFeeRatio = sysConfigService.getValue(Config.Sys.Withdraw.提现手续费比例);
        BigDecimal saleWithdrawSaleFeeRatio = sysConfigService.getValue(Config.Sys.Withdraw.优惠提现手续费比例);

        // 提现手续费 = 市场收入 * 优惠手续费比例 + 剩余提现金额 * 提现手续费比例
        if(withdraw.getAmount().compareTo(wallet.getFreeWithdrawAmount()) > 0){
            useFreeAmount = wallet.getFreeWithdrawAmount();
            // 计算正常提现手续费
            BigDecimal saleWithdrawFee = useFreeAmount.multiply(saleWithdrawSaleFeeRatio);
            BigDecimal normalWithdrawFee = withdraw.getAmount().subtract(useFreeAmount).multiply(withdrawFeeRatio);
            totalWithdrawFee = normalWithdrawFee.add(saleWithdrawFee).setScale(2, RoundingMode.DOWN);
        }else{
            useFreeAmount = withdraw.getAmount();
            // 没有需要计算正常手续费的额度
            totalWithdrawFee = useFreeAmount.multiply(saleWithdrawSaleFeeRatio).setScale(2, RoundingMode.DOWN);
        }
        withdraw.setWithdrawFee(totalWithdrawFee);
        withdraw.setWithdrawFeeRatio(withdrawFeeRatio);
        withdraw.setFreeWithdrawAmount(useFreeAmount);
        withdraw.setActuallyArriveAmount(withdraw.getAmount().subtract(totalWithdrawFee));

        // 提现冻结
        save(withdraw);
        userWalletService.withdrawFreeze(wallet.getUserId(), UserWalletEnum.CASH, withdraw.getAmount(), withdraw.getFreeWithdrawAmount(), WalletChangeTypeEnum.WITHDRAW, withdraw.getId(), "用户提现");
        // 增加进度记录
        userWalletWithdrawHandlerService.addRecord(withdraw.getId(), WithdrawStatusEnum.WAIT_PROCESS, null);
    }

    /**
     * 提现处理
     */
    @RedisLock(lockKey = LockKey.LOCK_WITHDRAW_STATUS)
    @Transactional(rollbackFor = Exception.class)
    public void withdrawHandler(@LockValue Long id, WithdrawStatusEnum status, String opinion) {
        SysUserWalletWithdraw withdraw = getById(id);
        Objects.requireNonNull(withdraw, "提现不存在");
        switch (status) {
            case PROCESS_ABORT:
            case PROCESS_PASS:
                if (withdraw.getStatus() != WithdrawStatusEnum.WAIT_PROCESS) {
                    throw new ServiceException("当前处理类型错误，用户提现状态为：" + withdraw.getStatus().getMsg());
                }
                break;
            default:
                throw new ServiceException("处理状态类型错误");
        }

        if (status == WithdrawStatusEnum.PROCESS_ABORT) {
            userWalletService.withdrawAbort(withdraw.getUserId(), UserWalletEnum.CASH, withdraw.getAmount(), withdraw.getFreeWithdrawAmount(), WalletChangeTypeEnum.WITHDRAW, id, "提现驳回");
        }
        if (status == WithdrawStatusEnum.PROCESS_PASS) {
            //校验用户钱包记录与当前余额是否一致
            CheckUserWalletVo checkUserWalletVo = baseMapper.checkWalletAmount(withdraw.getUserId(), UserWalletEnum.CASH.getValue());
            if (Objects.isNull(checkUserWalletVo) || checkUserWalletVo.getWalletAmount().compareTo(checkUserWalletVo.getRecordAmount()) != 0) {
                throw new ServiceException("1余额不足！");
            }
            userWalletService.withdrawFinish(withdraw.getUserId(), UserWalletEnum.CASH, withdraw.getAmount(), "用户提现结算");
        }

        boolean update = update(new LambdaUpdateWrapper<SysUserWalletWithdraw>()
                .eq(SysUserWalletWithdraw::getId, id)
                .set(SysUserWalletWithdraw::getStatus, status)
        );
        if (!update) {
            throw new ServiceException("操作失败！");
        }
        userWalletWithdrawHandlerService.addRecord(id, status, opinion);
    }

    /**
     * 用户提现列表
     *
     * @param params
     * @return
     */
    public List<WithdrawListVo> getVoList(GetWithdrawListDto params) {
        return getBaseMapper().selectVoList(params);
    }

    /**
     * 用户提现详情
     *
     * @param id
     * @return
     */
    public WithdrawListVo getDetail(Long id) {
        WithdrawListVo vo = getBaseMapper().getVoById(id);
        if (Objects.isNull(vo)) {
            throw new ServiceException("资源不存在！");
        }
        return vo;
    }

    /**
     * 获取今日提现金额（包含审核中）
     *
     * @param userId
     * @return
     */
    public BigDecimal getSumWithdraw(Long userId, List<WithdrawStatusEnum> status, LocalDate date) {
        return baseMapper.getUserWithdrawAmount(userId, status, date);
    }

    /**
     * 计算提现手续费
     */
    public AppWithdrawFeeVo calcWithdrawFee(Long userId, BigDecimal amount){
        AppWithdrawFeeVo vo = new AppWithdrawFeeVo();
        SysUserWallet wallet = userWalletService.getWallet(userId, UserWalletEnum.CASH);
        BigDecimal withdrawFeeRatio = sysConfigService.getValue(Config.Sys.Withdraw.提现手续费比例);
        BigDecimal saleWithdrawFeeRatio = sysConfigService.getValue(Config.Sys.Withdraw.优惠提现手续费比例);
        vo.setWithdrawFeeRatio(withdrawFeeRatio);
        vo.setAmount(amount);

        // 提现手续费
        BigDecimal totalWithdrawFee;
        // 使用的优惠提现额度
        BigDecimal useFreeAmount;
        if(amount.compareTo(wallet.getFreeWithdrawAmount()) > 0){
            useFreeAmount = wallet.getFreeWithdrawAmount();
            // 计算折扣的手续费
            BigDecimal saleWithdrawFee = useFreeAmount.multiply(saleWithdrawFeeRatio);
            // 计算剩余额度的正常提现手续费
            BigDecimal normalWithdrawFee = amount.subtract(useFreeAmount).multiply(withdrawFeeRatio);
            totalWithdrawFee = saleWithdrawFee.add(normalWithdrawFee).setScale(2, RoundingMode.DOWN);
        }else{
            useFreeAmount = amount;
            // 计算折扣的手续费
            totalWithdrawFee = useFreeAmount.multiply(saleWithdrawFeeRatio).setScale(2, RoundingMode.DOWN);
        }
        vo.setUseFreeAmount(useFreeAmount);
        vo.setWithdrawFee(totalWithdrawFee);
        vo.setActuallyArriveAmount(amount.subtract(totalWithdrawFee));
        return vo;
    }

    /**
     * 获取用户提现统计信息
     *
     * @param userId
     * @return
     */
    public AppWithdrawStaticInfoVo getWithdrawStatic(Long userId) {
        AppWithdrawStaticInfoVo vo = new AppWithdrawStaticInfoVo();
        vo.setWithdrawing(getSumWithdraw(userId, Collections.singletonList(WithdrawStatusEnum.WAIT_PROCESS), null));
        vo.setTodayWithdraw(getSumWithdraw(userId, Arrays.asList(WithdrawStatusEnum.WAIT_PROCESS, WithdrawStatusEnum.PROCESS_PASS), LocalDate.now()));
        vo.setTotalWithdraw(getSumWithdraw(userId, Arrays.asList(WithdrawStatusEnum.WAIT_PROCESS, WithdrawStatusEnum.PROCESS_PASS), null));
        BigDecimal dayWithdrawMax = sysConfigService.getValue(Config.Sys.Withdraw.当日提现金额上限);
        if(Objects.nonNull(dayWithdrawMax)){
            vo.setDayWithdrawMax(dayWithdrawMax);
            BigDecimal todayResidue = dayWithdrawMax.subtract(vo.getTodayWithdraw());
            if(todayResidue.compareTo(BigDecimal.ZERO) < 0){
                todayResidue = BigDecimal.ZERO;
            }
            vo.setTodayWithdrawResidue(todayResidue);
        }
        SysUserWallet wallet = userWalletService.getWallet(userId, UserWalletEnum.CASH);
        vo.setWalletBalance(wallet.getAmount());
        vo.setFreeWithdrawAmount(wallet.getFreeWithdrawAmount());
        vo.setWithdrawFeeRatio(sysConfigService.getValue(Config.Sys.Withdraw.提现手续费比例));
        vo.setWithdrawPrompts(sysConfigService.getValue(Config.Sys.Withdraw.提现提示语));
        return vo;
    }

    public R<WithdrawListTotalVo> withdrawListTotal(GetWithdrawListDto params) {
        WithdrawListTotalVo res = new WithdrawListTotalVo();

        params.setStatus(null);
        res.setAmount(baseMapper.selectVoListTotal(params));

        params.setStatus(WithdrawStatusEnum.PROCESS_PASS);
        res.setSettledAmount(baseMapper.selectVoListTotal(params));

        params.setStatus(WithdrawStatusEnum.WAIT_PROCESS);
        res.setReviewedAmount(baseMapper.selectVoListTotal(params));

        params.setStatus(WithdrawStatusEnum.PROCESS_ABORT);
        res.setOverruleAmount(baseMapper.selectVoListTotal(params));

        res.setUserAmount(baseMapper.getUserTotalAmount());


        return R.ok(res);
    }
    /**
     * 用户钱包提现导出
     * @param params
     * @return
     */
    public List<WithdrawListExportVo> export(GetWithdrawListDto params) {
        params.startOrder();
        params.setPageSize(null);
        List<WithdrawListVo> vos = getBaseMapper().selectVoList(params);
        List<WithdrawListExportVo> res = BeanUtil.copyToList(vos, WithdrawListExportVo.class);

        return res;
    }
}
